package sim1011.behaviours;

import jade.core.AID;
import jade.core.behaviours.CyclicBehaviour;
import jade.core.behaviours.ThreadedBehaviourFactory;
import jade.domain.DFService;
import jade.domain.FIPAException;
import jade.domain.FIPAAgentManagement.DFAgentDescription;
import jade.domain.FIPAAgentManagement.ServiceDescription;
import jade.lang.acl.ACLMessage;
import jade.lang.acl.MessageTemplate;
import sim1011.poltergeist.ResourceManager;
import sim1011.starter.EctoMobileStarter;
import sim1011.starter.Logger;

public class ResourceManagerBehaviour extends CyclicBehaviour {
	private static final long serialVersionUID = 5243336149328371577L;
	boolean moveAll = true;
	private AID[] poltergeist;

	@Override
	public void action() {
		MessageTemplate tp = MessageTemplate
				.MatchPerformative(ACLMessage.REQUEST_WHEN);
		ACLMessage msg = myAgent.receive(tp);
		if (msg != null) {
			Logger.log("[RESOURCEMANAGER " + myAgent.getLocalName()
					+ "] arrived message to decrease the battery");
			if (msg.getContent().equals("decrease battery") && ((ResourceManager) myAgent).getBatteryLevel() > 0.0f) {
				((ResourceManager) myAgent).setBatteryLevel(((ResourceManager) myAgent).getBatteryLevel()
								- EctoMobileStarter.DRAIN_POWER_WITH_VAMPIRE);
				Logger.log("[RESOURCEMANAGER " + myAgent.getLocalName()
						+ "] New battery level: "
						+ ((ResourceManager) myAgent).getBatteryLevel());
				if (((ResourceManager) myAgent).getBatteryLevel() < EctoMobileStarter.MOBILE_POWER_THRESHOLD && moveAll) {
					moveAll = false;
					Logger.log("[RESOURCEMANAGER "
							+ myAgent.getLocalName()
							+ "] Battery too low! Move out all components from mobile node!");
					ACLMessage moveAllComponents = new ACLMessage(ACLMessage.PROPAGATE);
					poltergeist = findPoltergeist();
					moveAllComponents.setContent("move out");
					moveAllComponents.addReceiver(poltergeist[0]);
					myAgent.send(moveAllComponents);
				}
			}
		} else {
			tp = MessageTemplate.MatchPerformative(ACLMessage.REQUEST);
			msg = myAgent.receive(tp);
			if (msg != null) {
				// Logger.log("[RESOURCEMANAGER " + myAgent.getLocalName() +
				// "] arrived message REQUEST from: " +
				// msg.getSender().getLocalName());
				ACLMessage resourceResponse = new ACLMessage(ACLMessage.CONFIRM);
				String responseContent = msg.getContent() + "#"
						+ ((ResourceManager) myAgent).getFreeCPU() + "#"
						+ ((ResourceManager) myAgent).getFreeMemory() + "#"
						+ ((ResourceManager) myAgent).getBatteryLevel() + "#"
						+ ((ResourceManager) myAgent).getCurrentCost() + "#"
						+ ((ResourceManager) myAgent).getCurrentThroughput();
				((ResourceManager) myAgent).resetThroughupt();
				Logger.log("[RESOURCEMANAGER " + myAgent.getLocalName()
						+ "] invio: " + responseContent);
				resourceResponse.setContent(responseContent);
				resourceResponse.addReceiver(msg.getSender());
				myAgent.send(resourceResponse);
			} else {
				// qui dovrebbe entrarci solo se riceve un messaggio con
				// performative != REQUEST_WHEN || REQUEST
				msg = myAgent.receive();
				if (msg != null) {
					// Logger.log("[RESOURCEMANAGER " + myAgent.getLocalName() +
					// "] arrived message");
					String[] resources = parseResourceMessage(msg.getContent());
					if (msg.getPerformative() == ACLMessage.CONFIRM) {
						// Richiesta di caricamento codice ed esecuzione
						Logger.log("[RESOURCEMANAGER " + myAgent.getLocalName()
								+ "] consume for: "
								+ msg.getSender().getLocalName());
						// ((ResourceManager)myAgent).consumeResource(resources[0],
						// resources[1]);
						ThreadedBehaviourFactory tbf = new ThreadedBehaviourFactory();
						ConsumeResourceBehaviour b = new ConsumeResourceBehaviour(
								Integer.parseInt(resources[0]), Integer.parseInt(resources[1]), msg.getSender());
						myAgent.addBehaviour(tbf.wrap(b));
					} else if(msg.getPerformative() == ACLMessage.UNKNOWN) {
						float consume = ((ResourceManager)myAgent).consumeBatteryOnMigration(Integer.parseInt(resources[0]),
								Integer.parseInt(resources[1]), Integer.parseInt(resources[2]));
						Logger.log("[RESOURCEMANAGER " + myAgent.getLocalName()
								+ "] consuming " + consume + "% battery to migrate component: "
								+ msg.getSender().getLocalName());
						((ResourceManager)myAgent).setBatteryLevel(((ResourceManager)myAgent).getBatteryLevel() 
								- consume);
					}
				} else {
					block();
				}
			}
		}
		block();
	}

	private AID[] findPoltergeist() {
		DFAgentDescription template = new DFAgentDescription();
		ServiceDescription sd = new ServiceDescription();
		sd.setType("ecto-poltergeist");
		template.addServices(sd);
		try {
			DFAgentDescription[] result = DFService.search(myAgent, template);
			poltergeist = new AID[result.length];
			for (int i = 0; i < result.length; ++i)
				// dovrebbe essere uno solo
				poltergeist[i] = result[i].getName();
			return poltergeist;
		} catch (FIPAException e) {
			e.printStackTrace();
		}
		return null;
	}

	private String[] parseResourceMessage(String message) {
		// la prima entry è la CPU, la seconda la memory
		/*StringTokenizer resourceTokens = new StringTokenizer(message, "#");
		int i = 0;
		int[] resources = new int[2];
		while (resourceTokens.hasMoreTokens()) {
			resources[i] = Integer.parseInt(resourceTokens.nextToken());
			i++;
		}*/
		return message.split("#");
	}
}
